/**
 * 
 */
package com.novartis.nims.trialsubmitter;

import java.awt.event.KeyEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.SwingUtilities;
import java.awt.Point;

import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JMenuItem;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JFrame;
import javax.swing.JDialog;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
//import org.rsna.util.HtmlJPanel;
import com.novartis.nims.util.HtmlJPanel;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Toolkit;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.awt.Insets;
import javax.swing.BoxLayout;
import java.awt.Font;
import java.awt.FlowLayout;
import javax.swing.BorderFactory;
import javax.swing.border.EtchedBorder;


/**
 * @author snydejo1
 *
 */
public class Application {
	
	private ApplicationConfiguration mConfiguration = null;

	private JFrame jFrame = null;  //  @jve:decl-index=0:visual-constraint="10,10"
	private JPanel jContentPane = null;
	private JMenuBar jJMenuBar = null;
	private JMenu fileMenu = null;
	private JMenu helpMenu = null;
	private JMenuItem exitMenuItem = null;
	private JMenuItem usageMenuItem = null;
	private JDialog usageDialog = null;
	private HtmlJPanel usageContentPane = null;
	private JLabel mInputLabel = null;
	private JTextField mInputField = null;
	private JLabel mOutputLabel = null;
	private JTextField mOutputField = null;
	private JButton mExecuteButton = null;
	private JButton mInputBrowseButton = null;
	private JButton mOutputBrowseButton = null;
	private JSpinner mSubmissionSpinner = null;

	private JPanel mInputPane = null;

	private JPanel mButtonPane = null;

	private JPanel mSubmissionPanel = null;

	private JLabel mSubmissionNumberLabel = null;
	/**
	 * 
	 */
	public Application() {
		try {
			mConfiguration = new ApplicationConfiguration();
		} catch (ApplicationConfigurationException e) {
			showErrorMessage(e.getMessage());
			System.exit(1);
		}
	}

	/**
	 * This method initializes mInputField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getMInputField() {
		if (mInputField == null) {
			mInputField = new JTextField();
		}
		return mInputField;
	}

	/**
	 * This method initializes mOutputField	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getMOutputField() {
		if (mOutputField == null) {
			mOutputField = new JTextField();
		}
		return mOutputField;
	}

	/**
	 * This method initializes mExecuteButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getMExecuteButton() {
		if (mExecuteButton == null) {
			mExecuteButton = new JButton();
			mExecuteButton.setText("Execute");
			mExecuteButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent event) {
					try {
						checkInput();
					} catch (UserException e) {
						showErrorMessage(e.getMessage());
						return;
					}
					
					Logger logger = Logger.getRootLogger();
					logger.removeAllAppenders();
					try {
						logger.addAppender(new FileAppender(
								new PatternLayout("%d{HH:mm:ss} %-5p [%c{1}] %m%n"),
								mOutputField.getText() + File.separator + "nits.log"));
					} catch (IOException e) {
						JOptionPane.showMessageDialog(jFrame,
								"Couldn't open file for logging. Try making sure the output directory is empty and writeable.",
								"Error",
								JOptionPane.ERROR_MESSAGE);
						return;
					}
					logger.setLevel(Level.INFO);
					
					//generate trial from input directory
					Trial trial = new Trial(mInputField.getText(),
							mOutputField.getText(),
							String.valueOf(((SpinnerNumberModel)mSubmissionSpinner.getModel()).getNumber().intValue()),
							mConfiguration);
					
					TrialRegistrar registrar = new TrialRegistrar(trial);
					TrialRegistrarProgressMonitor registrarMonitor = new TrialRegistrarProgressMonitor(
							registrar,
							jFrame,
							"Adding subjects to trial submission.",
							"sdfds", 0, 100);
					registrarMonitor.setMillisToPopup(0);
					registrarMonitor.setMillisToDecideToPopup(0);
					
					//registrarMonitor will listen for property changes fired by registrar
					//when registrarMonitor realizes registrar is done executing, it will
					//respond to any exceptions that were thrown.
					logger.info("Starting execution.");
					registrar.execute();
				}
			});
		}
		return mExecuteButton;
	}

	/**
	 * This method initializes mInputBrowseButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getMInputBrowseButton() {
		if (mInputBrowseButton == null) {
			mInputBrowseButton = new JButton();
			mInputBrowseButton.setText("Browse");
			mInputBrowseButton.setPreferredSize(new Dimension(78, 26));
			mInputBrowseButton.addActionListener(new JFileChooserActionListener(jFrame, mInputField));
		}
		return mInputBrowseButton;
	}

	/**
	 * This method initializes mOutputBrowseButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getMOutputBrowseButton() {
		if (mOutputBrowseButton == null) {
			mOutputBrowseButton = new JButton();
			mOutputBrowseButton.setText("Browse");
			mOutputBrowseButton.setMnemonic(KeyEvent.VK_UNDEFINED);
			mOutputBrowseButton.setPreferredSize(new Dimension(78, 26));
			mOutputBrowseButton.addActionListener(new JFileChooserActionListener(jFrame, mOutputField));
		}
		return mOutputBrowseButton;
	}

	/**
	 * This method initializes mSubmissionSpinner	
	 * 	
	 * @return javax.swing.JSpinner	
	 */
	private JSpinner getMSubmissionSpinner() {
		if (mSubmissionSpinner == null) {
			mSubmissionSpinner = new JSpinner();
			mSubmissionSpinner.setFont(new Font("Dialog", Font.PLAIN, 12));
			SpinnerModel model = new SpinnerNumberModel(1, 1, 100, 1);
			mSubmissionSpinner.setModel(model);
			
		}
		return mSubmissionSpinner;
	}

	/**
	 * This method initializes mInputPane	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getMInputPane() {
		if (mInputPane == null) {
			GridBagConstraints gridBagConstraints51 = new GridBagConstraints();
			gridBagConstraints51.gridx = 0;
			gridBagConstraints51.anchor = GridBagConstraints.WEST;
			gridBagConstraints51.insets = new Insets(0, 0, 0, 0);
			gridBagConstraints51.gridy = 4;
			GridBagConstraints gridBagConstraints6 = new GridBagConstraints();
			gridBagConstraints6.insets = new Insets(0, 2, 2, 2);
			gridBagConstraints6.gridy = 3;
			gridBagConstraints6.anchor = GridBagConstraints.WEST;
			gridBagConstraints6.gridx = 1;
			GridBagConstraints gridBagConstraints5 = new GridBagConstraints();
			gridBagConstraints5.insets = new Insets(0, 2, 2, 2);
			gridBagConstraints5.gridy = 1;
			gridBagConstraints5.ipadx = 0;
			gridBagConstraints5.ipady = 0;
			gridBagConstraints5.anchor = GridBagConstraints.WEST;
			gridBagConstraints5.gridx = 1;
			GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
			gridBagConstraints3.fill = GridBagConstraints.BOTH;
			gridBagConstraints3.gridwidth = 1;
			gridBagConstraints3.gridx = 0;
			gridBagConstraints3.gridy = 3;
			gridBagConstraints3.weightx = 1.0;
			gridBagConstraints3.anchor = GridBagConstraints.WEST;
			gridBagConstraints3.insets = new Insets(0, 2, 2, 2);
			GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
			gridBagConstraints2.anchor = GridBagConstraints.WEST;
			gridBagConstraints2.gridx = 0;
			gridBagConstraints2.gridy = 2;
			gridBagConstraints2.insets = new Insets(2, 2, 0, 2);
			GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
			gridBagConstraints1.fill = GridBagConstraints.BOTH;
			gridBagConstraints1.gridwidth = 1;
			gridBagConstraints1.gridx = 0;
			gridBagConstraints1.gridy = 1;
			gridBagConstraints1.ipadx = 0;
			gridBagConstraints1.ipady = 0;
			gridBagConstraints1.weightx = 1.0;
			gridBagConstraints1.anchor = GridBagConstraints.WEST;
			gridBagConstraints1.insets = new Insets(0, 2, 2, 2);
			GridBagConstraints gridBagConstraints = new GridBagConstraints();
			gridBagConstraints.anchor = GridBagConstraints.WEST;
			gridBagConstraints.gridx = 0;
			gridBagConstraints.gridy = 0;
			gridBagConstraints.ipadx = 0;
			gridBagConstraints.ipady = 0;
			gridBagConstraints.insets = new Insets(2, 2, 0, 2);
			mInputPane = new JPanel();
			mInputPane.setLayout(new GridBagLayout());
			mInputPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
			mInputPane.add(mInputLabel, gridBagConstraints);
			mInputPane.add(getMInputField(), gridBagConstraints1);
			mInputPane.add(mOutputLabel, gridBagConstraints2);
			mInputPane.add(getMOutputField(), gridBagConstraints3);
			mInputPane.add(getMInputBrowseButton(), gridBagConstraints5);
			mInputPane.add(getMOutputBrowseButton(), gridBagConstraints6);
			mInputPane.add(getMSubmissionPanel(), gridBagConstraints51);
		}
		return mInputPane;
	}

	/**
	 * This method initializes mButtonPane	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getMButtonPane() {
		if (mButtonPane == null) {
			FlowLayout flowLayout = new FlowLayout();
			flowLayout.setAlignment(FlowLayout.RIGHT);
			GridBagConstraints gridBagConstraints4 = new GridBagConstraints();
			gridBagConstraints4.fill = GridBagConstraints.BOTH;
			gridBagConstraints4.gridx = 0;
			gridBagConstraints4.gridy = 0;
			gridBagConstraints4.insets = new Insets(2, 2, 0, 2);
			mButtonPane = new JPanel();
			mButtonPane.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
			mButtonPane.setLayout(flowLayout);
			mButtonPane.add(getMExecuteButton(), null);
		}
		return mButtonPane;
	}

	/**
	 * This method initializes mSubmissionPanel	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getMSubmissionPanel() {
		if (mSubmissionPanel == null) {
			FlowLayout flowLayout1 = new FlowLayout();
			flowLayout1.setAlignment(FlowLayout.LEFT);
			flowLayout1.setVgap(2);
			flowLayout1.setHgap(2);
			mSubmissionNumberLabel = new JLabel();
			mSubmissionNumberLabel.setText("Submission number");
			mSubmissionPanel = new JPanel();
			mSubmissionPanel.setLayout(flowLayout1);
			mSubmissionPanel.add(mSubmissionNumberLabel, null);
			mSubmissionPanel.add(getMSubmissionSpinner(), null);
		}
		return mSubmissionPanel;
	}

	/**
	 * @param args
	 * @throws UnsupportedLookAndFeelException 
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 * @throws ClassNotFoundException 
	 */
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				Application application = new Application();
				application.getJFrame().setVisible(true);
			}
		});
	}

	/**
	 * This method initializes jFrame
	 * 
	 * @return javax.swing.JFrame
	 */
	private JFrame getJFrame() {
		if (jFrame == null) {
			jFrame = new JFrame();
			jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			jFrame.setJMenuBar(getJJMenuBar());
			jFrame.setSize(521, 210);
			jFrame.setContentPane(getJContentPane());
			jFrame.setTitle("Imaging Trial Submitter");
		}
		return jFrame;
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			GridBagConstraints gridBagConstraints21 = new GridBagConstraints();
			gridBagConstraints21.fill = GridBagConstraints.BOTH;
			gridBagConstraints21.gridy = 4;
			gridBagConstraints21.weightx = 1.0;
			gridBagConstraints21.anchor = GridBagConstraints.WEST;
			gridBagConstraints21.insets = new Insets(2, 2, 2, 2);
			gridBagConstraints21.gridx = 1;
			mOutputLabel = new JLabel();
			mOutputLabel.setText("Location of empty directory to save trial submission");
			mInputLabel = new JLabel();
			mInputLabel.setText("Location of directory containing subject directories");
			mInputLabel.setDisplayedMnemonic(KeyEvent.VK_UNDEFINED);
			jContentPane = new JPanel();
			jContentPane.setLayout(new BoxLayout(getJContentPane(), BoxLayout.Y_AXIS));
			jContentPane.add(getMInputPane(), null);
			jContentPane.add(getMButtonPane(), null);
		}
		return jContentPane;
	}

	/**
	 * This method initializes jJMenuBar	
	 * 	
	 * @return javax.swing.JMenuBar	
	 */
	private JMenuBar getJJMenuBar() {
		if (jJMenuBar == null) {
			jJMenuBar = new JMenuBar();
			jJMenuBar.add(getFileMenu());
			jJMenuBar.add(getHelpMenu());
		}
		return jJMenuBar;
	}

	/**
	 * This method initializes jMenu	
	 * 	
	 * @return javax.swing.JMenu	
	 */
	private JMenu getFileMenu() {
		if (fileMenu == null) {
			fileMenu = new JMenu();
			fileMenu.setText("File");
			fileMenu.add(getExitMenuItem());
		}
		return fileMenu;
	}

	/**
	 * This method initializes jMenu	
	 * 	
	 * @return javax.swing.JMenu	
	 */
	private JMenu getHelpMenu() {
		if (helpMenu == null) {
			helpMenu = new JMenu();
			helpMenu.setText("Help");
			helpMenu.add(getUsageMenuItem());
		}
		return helpMenu;
	}

	/**
	 * This method initializes jMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getExitMenuItem() {
		if (exitMenuItem == null) {
			exitMenuItem = new JMenuItem();
			exitMenuItem.setText("Exit");
			exitMenuItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					System.exit(0);
				}
			});
		}
		return exitMenuItem;
	}

	/**
	 * This method initializes jMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getUsageMenuItem() {
		if (usageMenuItem == null) {
			usageMenuItem = new JMenuItem();
			usageMenuItem.setText("Usage");
			usageMenuItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					JDialog usageDialog = getUsageDialog();
					usageDialog.pack();
					Point loc = getJFrame().getLocation();
					loc.translate(20, 20);
					usageDialog.setLocation(loc);
					usageDialog.setVisible(true);
				}
			});
		}
		return usageMenuItem;
	}

	/**
	 * This method initializes aboutDialog	
	 * 	
	 * @return javax.swing.JDialog
	 */
	private JDialog getUsageDialog() {
		if (usageDialog == null) {
			usageDialog = new JDialog(getJFrame(), true);
			usageDialog.setTitle("Usage");
			usageDialog.setContentPane(getUsageContentPane());
		}
		return usageDialog;
	}

	/**
	 * This method initializes aboutContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getUsageContentPane() {
		if (usageContentPane == null) {
			InputStream is = ApplicationConfiguration.getResourceAsStream("usage.html");
			String defaultUsage = "<html>No usage information available.</html>";
			if (is == null) {
				usageContentPane = new HtmlJPanel(defaultUsage);
			} else {
				String usage = getInputStreamText(is);
				try { is.close(); } catch (IOException e) { }
				
				if (usage.isEmpty()) {
					usage = defaultUsage;
				}
				usageContentPane = new HtmlJPanel(usage);
			}
			
			Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
			dim.setSize(dim.width * 2.0 / 3.0, dim.height * 2.0 / 3.0);
			usageContentPane.setPreferredSize(dim);
			usageContentPane.scrollToTop();
			usageContentPane.scrollToLeft();
			
		}
		return usageContentPane;
	}
	

	/**
	 * Read a text input stream completely, using the latin1 encoding.
	 * @param is the input stream to read.
	 * @return the text of the input stream, or an empty string if an error occurred.
	 */
	private static String getInputStreamText(InputStream is) {
		Charset latin1 = Charset.forName("iso-8859-1");
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(is, latin1));
			StringWriter sw = new StringWriter();
			int n;
			char[] cbuf = new char[1024];
			while ((n=br.read(cbuf,0,cbuf.length)) != -1) sw.write(cbuf,0,n);
			br.close();
			return sw.toString();
		}
		catch (Exception e) { return ""; }
	}

	private void checkInput() throws UserException {
		String directoryString = mInputField.getText();
		
		File inputFile = new File(directoryString);
		if (! inputFile.exists()) {
			throw new UserException("Specified source directory does not exist. " +
				"Try using the Browse button to select a location.");
		}
		
		if (! inputFile.isDirectory()) {
			throw new UserException("Specified source directory is actually a file." +
				"Try using the Browse button to select a location.");
		}
		
		if (inputFile.list().length < 1) {
			throw new UserException("Specified source directory is empty." +
				"Try selecting a location that contains image files in one of its sub-directories.");
		}

		
		directoryString = mOutputField.getText();
		File outputFile = new File(directoryString);
		
		if (! outputFile.exists()) {
			throw new UserException("Specified submission directory does not exist. " +
				"Try using the Browse button to select a location.");
		}
		
		if (! outputFile.isDirectory()) {
			throw new UserException("Specified submission directory is actually a file. " +
				"Try using the Browse button to select a location.");
		}
		
		if (outputFile.list() == null) {
			throw new UserException("Specified submission directory is actually " +
					"a file or is unreadable. Try using the Browse button to " +
					"select a location.");
		}
		if (outputFile.list().length == 1) {
			if (outputFile.list()[0].equalsIgnoreCase("nits.log")) {
				//do nothing, log file will be appended to
			}
		} else if (outputFile.list().length > 1) {
			throw new UserException("Specified submission directory isn't empty. " +
				"Try selecting a location that doesn't contain any files or sub-directories.");
		}
		
		if (! outputFile.canWrite()) {
			throw new UserException("You can't write to specified submission directory. " +
				"Try changing the permissions on it, or selecting a new location.");
		}
	}
	
	private class JFileChooserActionListener implements ActionListener {
		private Component mParent;
		private JTextField mTextField;
		
		private JFileChooserActionListener(Component parent, JTextField field) {
			mParent = parent;
			mTextField = field;
		}

		public void actionPerformed(ActionEvent e) {
			// Create a file chooser
			final JFileChooser chooser = new JFileChooser();
			chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			chooser.setCurrentDirectory(new File(mTextField.getText()));
			int returnVal = chooser.showOpenDialog(mParent);
		    if (returnVal == JFileChooser.APPROVE_OPTION) {
		    	mTextField.setText(chooser.getSelectedFile().getAbsolutePath());
		    }
		}
	}
	
	private class TrialRegistrarProgressMonitor extends ModalProgressMonitor implements PropertyChangeListener {
		private TrialRegistrar mWorker;
		private boolean mFinishedWorkerIsProcessed = false;
		
		private TrialRegistrarProgressMonitor(TrialRegistrar worker, Component parentComponent, Object message, String note, int min, int max) {
			super(parentComponent, message, note, min, max);
			mWorker = worker;
			mWorker.addPropertyChangeListener(this);
		}

		public void propertyChange(PropertyChangeEvent evt) {
			if ("progress" == evt.getPropertyName()) {
				if (!mWorker.isDone()) {
					if (this.isCanceled()) {
						// if user has canceled, then cancel the worker
						mWorker.cancel(true);
					} else { //otherwise update the progress monitor
						int progress = (Integer) evt.getNewValue();
						this.setProgress(progress);
					}
				}
	        } else if ("stage" == evt.getPropertyName()) {
        		TrialRegistrar.StageValue stage = (TrialRegistrar.StageValue) evt.getNewValue();
        		
        		if (stage == TrialRegistrar.StageValue.CALCULATE_SIZE) {
        			this.setNote("Calculating size of subject directories...");
    				this.setIndeterminate(true);
        		} else if (stage == TrialRegistrar.StageValue.ASSOCIATE_KEYS) {
        			this.setNote("Associating image files with clinical trial keys...");
    				this.setIndeterminate(false);
        		} else if (stage == TrialRegistrar.StageValue.CLEAN_IMAGES) {
        			this.setNote("Anonymizing images and copying to trial submission directory...");
    				this.setIndeterminate(false);
        		} else if (stage == TrialRegistrar.StageValue.MAKE_ODM) {
        			this.setNote("Saving ODM...");
    				this.setIndeterminate(false);
        		}
        	} else if ("state" == evt.getPropertyName()) {
	        	if (mWorker.isDone() && !(mFinishedWorkerIsProcessed)) {
	        		mFinishedWorkerIsProcessed = true;
	        		
	                Toolkit.getDefaultToolkit().beep();
	                
	                try {
	                	mWorker.get();
	                	Logger.getRootLogger().info("Execution finished successfully.");

	            		JOptionPane.showMessageDialog(jFrame,
	            				"Finished successfully!",
	            				"Info",
	            				JOptionPane.INFORMATION_MESSAGE);
	                } catch (CancellationException e) {
						Logger.getRootLogger().info("Execution finished due to user cancelation.");
	                } catch (InterruptedException e) {
	                	// do nothing
	                } catch (ExecutionException e) {
						Throwable cause = e.getCause();
						if (cause == null) cause = e;
						
						String message;
						if (cause instanceof TrialRegistrarException) {
							message = cause.getMessage();
						} else {
							message = cause.toString();
						}
						
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						e.printStackTrace(pw);
						pw.flush();
						Logger.getRootLogger().error(sw.toString());
						pw.close();
						Logger.getRootLogger().info("Execution finished due to error.");

						showErrorMessage(message);
                	} finally {
                		Logger.getRootLogger().removeAllAppenders();
                		this.close();
                	}
	        	}
	        }
		}
	}
	
	private void showErrorMessage(String message) {
		Object feedback = message;
		
		HtmlJPanel messagePane = new HtmlJPanel(message);
		
		String osName = System.getProperty("os.name");
		
		if (osName.contains("Windows")) {
			int maxWidth = (int) jFrame.getPreferredSize().getWidth();
			FontMetrics fm = messagePane.getFontMetrics(messagePane.getFont());
			int numberOfLines = 10;
			int height = (numberOfLines) * fm.getHeight();

			messagePane.setPreferredSize(new Dimension(maxWidth, height));
			messagePane.scrollToTop();
			messagePane.scrollToLeft();
			feedback = messagePane;
		}
		
		JOptionPane.showMessageDialog(jFrame,
				feedback,
				"Error",
				JOptionPane.ERROR_MESSAGE);
	}

}
